package com.example.jobback.aspect;
import com.example.jobback.util.CACHEUtil;
import com.example.jobback.util.TLUtil;
import jakarta.annotation.Resource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
 * @ClassName: RateLimitAspect
 * @Description: 流量限流控制切面
 * @Author: lhb
 * @Date: 2025/9/2
 */
@Aspect
@Component
public class RateLimitAspect {


    @Autowired
    private CACHEUtil cacheUtil;

    public static  final String RATE_LIMIT_KEY="rate_limit:slide:%s:%s:%s";

    // 切入点：拦截所有带@RateLimit注解的方法
    @Pointcut("@annotation(com.example.jobback.aspect.RateLimit)")
    public void rateLimitPointcut() {}

    @Before("rateLimitPointcut()")
    public void doBefore(JoinPoint joinPoint) {
        // 1. 获取当前用户ID（从线程本地变量中获取）
        Long userId = TLUtil.get(TLUtil.USER_ID);
        if (userId == null) {
            throw new RuntimeException("用户未登录，无法访问");
        }

        // 2. 生成接口唯一标识（用户ID + 类名 + 方法名）
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String className = method.getDeclaringClass().getName();
        String methodName = method.getName();
        String key = String.format(RATE_LIMIT_KEY, userId, className, methodName);


        // 3. 获取注解中的限流参数（时间窗口和最大请求数）
        RateLimit rateLimit = method.getAnnotation(RateLimit.class);
        int maxCount = rateLimit.count(); // 窗口内最大请求数
        long windowSize = rateLimit.time(); // 窗口大小（配合时间单位）
        TimeUnit unit = rateLimit.unit();
        // 转换窗口大小为毫秒（统一时间单位）
        long windowSizeMillis = unit.toMillis(windowSize);

        // 4. 滑动窗口核心逻辑
        long currentTime = System.currentTimeMillis(); // 当前时间戳（毫秒）
        // 计算窗口起始时间（当前时间 - 窗口大小）
        long windowStart = currentTime - windowSizeMillis;

        // 步骤1：移除窗口外的请求记录（只保留窗口内的时间戳）
        cacheUtil.removeZSetRangeByScore(key, 0, windowStart);

        // 步骤2：统计当前窗口内的请求数
        Long currentCount = cacheUtil.getZSetSize(key);


        if (currentCount != null && currentCount >= maxCount) {
            // 超过限制，抛出异常
            throw new RuntimeException("请求过于频繁，请稍后再试");
        }

        String code = UUID.randomUUID().toString();

        // 步骤3：将当前请求的时间戳加入集合（value用UUID避免重复）
        cacheUtil.setZSet(key, code, currentTime);

    }
}
